﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Caching.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations;
using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;

namespace Vit.Caching
{
    public class EntLibCacheMananger : ICacheManager
    {
        private string dependentEntitySetPrefix = "dependent_entity_set_";
        private CacheManager cacheManager = null;

        public EntLibCacheMananger()
        {
        }

        public EntLibCacheMananger(CacheManager cacheManager)
        {
            this.cacheManager = cacheManager;
        }

        public static CacheManager CreateCache(string cacheManagerName, int expirationPollFrequencyInSeconds, int maximumItemsInCacheBeforeScavenging, int numberToRemoveWhenScavenging)
        {
            DictionaryConfigurationSource internalConfigurationSource = new DictionaryConfigurationSource();
            CacheManagerSettings settings = new CacheManagerSettings();
            internalConfigurationSource.Add(CacheManagerSettings.SectionName, settings);
            CacheStorageData storageConfig = new CacheStorageData("Null Storage", typeof(NullBackingStore));
            settings.BackingStores.Add(storageConfig);
            CacheManagerData cacheManagerConfig = new CacheManagerData(cacheManagerName, expirationPollFrequencyInSeconds, maximumItemsInCacheBeforeScavenging, numberToRemoveWhenScavenging, storageConfig.Name);
            settings.CacheManagers.Add(cacheManagerConfig);
            settings.DefaultCacheManager = cacheManagerConfig.Name;
            Microsoft.Practices.EnterpriseLibrary.Caching.CacheManagerFactory cacheFactory = new Microsoft.Practices.EnterpriseLibrary.Caching.CacheManagerFactory(internalConfigurationSource);
            return cacheFactory.Create(cacheManagerName) as CacheManager;
        }

        #region ICacheManager Members

        public void Initialize(string cacheManagerName)
        {
            cacheManager = CreateCache(cacheManagerName, 60, 1000, 10);
            Name = cacheManagerName;
        }

        public void Add(string key, object value)
        {
            cacheManager.Add(key, value);
        }

        /// <summary>
        /// Adds the specified entry to the cache.
        /// </summary>
        /// <param name="key">The entry key.</param>
        /// <param name="value">The entry value.</param>
        /// <param name="dependentEntitySets">The list of dependent entity sets.</param>
        /// <param name="slidingExpiration">The sliding expiration - IGNORED.</param>
        /// <param name="absoluteExpiration">The absolute expiration.</param>
        public void Add(string key, object value, IEnumerable<string> dependentEntitySets, TimeSpan slidingExpiration, DateTime absoluteExpiration)
        {
            if (slidingExpiration == null || slidingExpiration == TimeSpan.Zero)
                slidingExpiration = TimeSpan.MaxValue;
            SlidingTime slidingTime = new SlidingTime(slidingExpiration);
            AbsoluteTime absTime = new AbsoluteTime(absoluteExpiration);

            foreach (string set in dependentEntitySets)
                EnsureDependancySet(key, dependentEntitySetPrefix + set);

            cacheManager.Add(key, value, CacheItemPriority.Normal, null, new ICacheItemExpiration[] { slidingTime, absTime });
        }

        public bool Contains(string key)
        {
            return cacheManager.Contains(key);
        }

        public int Count
        {
            get { return cacheManager.Count; }
        }

        public void Clear()
        {
            cacheManager.Flush();
        }

        public object GetData(string key)
        {
            return cacheManager.GetData(key);
        }

        public void Remove(string key)
        {
            cacheManager.Remove(key);
        }

        public void RemoveDependancySet(IEnumerable<string> sets)
        {
            foreach (string set in sets)
            {
                string dependentKey = dependentEntitySetPrefix + set;
                List<string> keys = GetData(dependentKey) as List<string>;
                if (keys != null)
                {
                    foreach (string key in keys)
                        Remove(key);
                }
                Remove(dependentKey);
            }
        }

        public object this[string key]
        {
            get { return cacheManager[key]; }
        }

        public string Name
        {
            get;
            private set;
        }

        #endregion

        private void EnsureDependancySet(string key, string dependancySet)
        {
            if (Contains(dependancySet) == false)
            {
                try
                {
                    List<string> keys = new List<string>();
                    keys.Add(key);
                    cacheManager.Add(dependancySet, keys, CacheItemPriority.Normal, null, new ICacheItemExpiration[] { new NeverExpired() });
                }
                catch
                {
                    // ignore exceptions.
                }
            }
            else
            {
                try
                {
                    List<string> keys = GetData(dependancySet) as List<string>;
                    if (keys.Contains(key) == false)
                        keys.Add(key);
                    cacheManager.Add(dependancySet, keys, CacheItemPriority.Normal, null, new ICacheItemExpiration[] { new NeverExpired() });
                }
                catch
                {
                    // ignore exceptions.
                }
            }
        }
    }
}
